﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using Sinacor.Infra.UI.Controls.SmartClient;
using System.Windows.Threading;
using System.Windows;
using System.Diagnostics;
using EL = Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using System.IO;
using Sinacor.Infra.UI.Desktop.SmartClient;
using Sinacor.Infra.UI.Session;
using Sinacor.Infra.UI.Messaging.Internal;
using Sinacor.Infra.UI.Messaging;
using Sinacor.Infra.UI.Messaging.SmartClient;
using Sinacor.Infra.UI.Exceptions;
using Sinacor.Infra.Common.Exceptions;
using PSE.Framework.ErrorLogging;
using PSE.Framework.UI.Resources;
using Sinacor.UI.Application.Settings;
using System.Configuration;

namespace Sinacor.UI.Application.Menu.SmartClient
{
    public struct NewWindow
    {
        public string AssemblyName;
        public string ClassName;
        public List<string> MenuPath;
        public string Role;
    }

    public class WindowController : DispatcherObject
    {
        public bool OpenWindow(string assembly, string className, List<string> menuPath, string role, Sinacor.UI.Application.Menu.Model.Company company)
        {
            try
            {
                if (assembly == "CenturaReport")
                {
                    this.OpenCenturaReport(className, company);
                }
                else
                {
                    // Instancia uma nova Thread
                    Thread newWindowThread = new Thread(new ParameterizedThreadStart(SinacorWindow_ParameterizedThreadStart));
                    //Define o ApartmentState como STA (requisito do WPF)
                    newWindowThread.SetApartmentState(ApartmentState.STA);

                    // Chama a função que inicia a nova Thread

                    NewWindow nw = new NewWindow();
                    nw.AssemblyName = assembly;
                    nw.ClassName = className;
                    nw.MenuPath = menuPath;
                    nw.Role = role;

                    //Inicializa a thread.
                    newWindowThread.Start(nw);
                }
            }
            catch (Exception ex)
            {
                HandleException(ex);
                return false;
            }

            return true;
        }

        private void SinacorWindow_ParameterizedThreadStart(object state)
        {
            try
            {
                NewWindow st = (NewWindow)state;
                this.InitializeWindow(RetrieveModuleName(st.AssemblyName), st.AssemblyName, st.ClassName, st.MenuPath, st.Role);
            }
            catch (Exception ex)
            {
                HandleException(ex);
            }
        }

        private void InitializeWindow(string moduleName, string assemblyName, string className, List<string> menuPath, string role)
        {
            Assembly assembly = null;
            Type typWindowType = null;
            object window = null;


            string windowPath = new Uri(new Uri(System.Windows.Forms.Application.StartupPath + @"\"),
                                           moduleName + @"/" + assemblyName).LocalPath;

            // Carrega o Assembly que contém a Janela a partir do disco
            assembly = Assembly.LoadFrom(windowPath);
            //assembly = Assembly.LoadFrom(moduleName + "\\" + assemblyName);


            // Obtém o tipo da Janela
            string fullAssemblyName = assemblyName.Replace(".dll", "") + "." + className;
            typWindowType = assembly.GetType(fullAssemblyName);
            bool windowIsOpened = false;

            if (typWindowType == null)
            {
                throw new Exception(string.Format("The class '{0}' was not found in the assembly '{1}'", className, assemblyName));
            }
            else
            {
                DesktopVirtual _virtualDesktop = DesktopVirtual.GetInstance();
                SessionManager _sessionaManager = SessionManager.GetInstance();
                WPFMessageManager _messageManager = new WPFMessageManager();

                foreach (SinacorWindow w in _virtualDesktop.GetDesktopVirtual(_sessionaManager.GetCurrentSession().TokenID))
                {
                    if (w.GetType() == typWindowType)
                    {
                        if (IsWindowOwnerNull(w) && !WindowAcceptsMultipleInstances(w))
                        {
                            windowIsOpened = true;

                            InternalMessage message = new InternalMessage();
                            message.MessageId = MessageIds.WindowManager.SNC_WINDOW_RESTORE;
                            message.Message = w.GetHashCode();
                            _messageManager.SendMessage(message);
                            break;
                        }
                    }
                }

                //liberar as intancias
                _virtualDesktop = null;
                _sessionaManager = null;
                _messageManager = null;
            }

            if (!windowIsOpened)
            {

                // Define parâmetros que serão usados pelo Constructor
                Type[] ctorParameter = Type.EmptyTypes;
                // Obtém informações sobre o Constructor
                ConstructorInfo ctor = typWindowType.GetConstructor(ctorParameter);
                // Instancia a nova janela
                window = ctor.Invoke(new object[] { });


                //Armazena a Janela.
                SinacorWindow sinacorWindow = (SinacorWindow)window;
                sinacorWindow.MenuHeader = menuPath;
                sinacorWindow.Role = role;
                sinacorWindow.Show();
                sinacorWindow.RunDispatcher();
                sinacorWindow = null;
            }

        }

        private bool IsWindowOwnerNull(SinacorWindow window)
        {
            if (System.Threading.Thread.CurrentThread != window.Dispatcher.Thread)
            {
                return (bool)window.Dispatcher.Invoke(new Func<SinacorWindow, bool>(IsWindowOwnerNull), window);
            }
            else
                return (window.Owner == null);
        }

        private bool WindowAcceptsMultipleInstances(SinacorWindow window)
        {
            if (System.Threading.Thread.CurrentThread != window.Dispatcher.Thread)
            {
                return (bool)window.Dispatcher.Invoke(new Func<SinacorWindow, bool>(WindowAcceptsMultipleInstances), window);
            }
            else
            {
                return window.AcceptsMultipleInstance;
            }
        }

        //private string RetrieveAssemblyFullPath(string assemblyName)
        //{
        //    //Sinacor.UI.Modulo.SubModulo.View.SmartClient
        //    if (!string.IsNullOrEmpty(assemblyName))
        //        return RetrieveModuleName(assemblyName) + @"\" + assemblyName;
        //    else
        //        return null;

        //}

        private static string RetrieveModuleName(string assemblyName)
        {
            return assemblyName.Split('.')[2];
        }

        /// <summary>
        /// Returns a text representation of the exception.
        /// </summary>
        /// <param name="exception">The exception to be formatted.</param>
        /// <returns>Exception formatted as a text.</returns>
        //private string FormatExceptionMessage(Exception exception)
        //{
        //    using (StringWriter sw = new StringWriter())
        //    {
        //        EL.TextExceptionFormatter exceptionFormatter = new EL.TextExceptionFormatter(sw, exception);
        //        exceptionFormatter.Format();
        //        return sw.ToString();
        //    }
        //}

        private void HandleException(Exception ex)
        {
            if (ex == null)
                throw new ArgumentNullException("ex");

            string message;
            Dictionary<string, object> extensionData = null;

            if (ex is TargetInvocationException &&
                ex.InnerException is BaseException)
                ex = ex.InnerException;

            ErrorMessage.ConfigureExceptionMessage(ex);

            if (ex is ValidationException ||
                ex is BaseErrorNumberException)
            {
                message = ex.Message;
            }
            // Outros Erros
            else
            {
                // Buscando mensagem
                message = (string)ResourceProviderManager.GetResourceProvider().TryGetResource("Common.Application.ExceptionMessage.WindowFailed");
            }

            Uri sourceAddress = null;

            if (ex is BaseLogException &&
                ((BaseLogException)ex).SourceAddress != null &&
                !string.IsNullOrEmpty(((BaseLogException)ex).LogData))
            {
                sourceAddress = ((BaseLogException)ex).SourceAddress;
                extensionData = new Dictionary<string, object>() { { "InnerLogData", ((BaseLogException)ex).LogData } };
            }

            LogData logData = Sinacor.Infra.UI.ErrorLogging.ErrorLogger.WriteLog(LogType.Error, ex, extensionData);

            SinacorMessageBox.ShowError(message, sourceAddress, logData);
        }

        private void OpenCenturaReport(string moduleId, Sinacor.UI.Application.Menu.Model.Company company)
        {
            ModulesConfigurationSection section = ConfigurationManager.GetSection("modules") as ModulesConfigurationSection;

            if (section == null)
            {
                SinacorMessageBox.Show("Configuration section 'modules' not found in 'SinacorApp.config'.", SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
                return;
            }

            ModuleConfigurationElement module = section.InstalledModules[moduleId];

            if (module == null)
            {
                SinacorMessageBox.Show(string.Format("Module '{0}' not found in section 'modules' of 'SinacorApp.config'.", moduleId), SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
                return;
            }

            if (string.IsNullOrEmpty(module.CenturaExecutable))
            {
                SinacorMessageBox.Show(string.Format("Centura executable is empty for module '{0}' in 'SinacorApp.config'.", moduleId), SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
                return;
            }

            if (string.IsNullOrEmpty(module.CenturaPath))
            {
                SinacorMessageBox.Show("Caminho do executável do Sinacor Centura não informado.\n\nPor favor informe um caminho válido utilizando o Configurador, disponível no menu 'Iniciar' > 'Sinacor' > 'Configurador do Sinacor', item 'Interface Gráfica' > 'Relatórios'.", SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
                return;
            }

            string centuraPath = Path.Combine(module.CenturaPath, module.CenturaExecutable);

            if (!File.Exists(centuraPath))
            {
                SinacorMessageBox.Show(string.Format("Não foi possível localizar o executável do Sinacor Centura no caminho '{0}'.\n\nPor favor informe um caminho válido utilizando o Configurador, disponível no menu 'Iniciar' > 'Sinacor' > 'Configurador do Sinacor', item 'Interface Gráfica' > 'Relatórios'.", centuraPath), SinacorMessageBoxButton.Ok, SinacorMessageBoxImage.Error);
                return;
            }

            Session session = Sinacor.Infra.UI.Session.SessionManager.GetInstance().GetCurrentSession();

            string dbInstance = company.DbInstance;
            string user = session.User.LoginName;
            int companyId = session.Company.Id;
            string dbUser = company.DbUser;

            Process process = new Process();
            process.StartInfo.WorkingDirectory = module.CenturaPath;
            process.StartInfo.FileName = module.CenturaExecutable;
            process.StartInfo.Arguments = string.Format("\"{0}\" \"{1}\" \"{2}\" \"{3}\"", dbInstance, user, companyId, dbUser);
            process.Start();

            //Process.Start(centuraPath, string.Format("\"{0}\" \"{1}\" \"{2}\" \"{3}\"", dbInstance, user, companyId, dbUser));
        }
    }
}
